﻿//========================================================= 
/**@file redis_extractor.h
 * @brief hiredis接口的简单封装
 * 
 * @date 2018-07-13   16:20:45
 * @author Zhyioo 
 * @version 1.0
 */ 
//--------------------------------------------------------- 
#ifndef _LIBZHOUYB_REDIS_EXTRACTOR_H_
#define _LIBZHOUYB_REDIS_EXTRACTOR_H_
//--------------------------------------------------------- 
#include "../../include/Base.h"

#include <hiredis/hiredis.h>

#include <vector>
//--------------------------------------------------------- 
namespace zhou_yb {
namespace wrapper {
namespace redis_extractor {
//--------------------------------------------------------- 
/**
 * @brief redis命令返回结果的封装
 * @date 2018-07-16 10:40
 */
class redis_reply
{
protected:
    shared_obj<redisReply*> _reply;
    bool _isInline;
public:
    redis_reply(void* p) : _reply(reinterpret_cast<redisReply*>(p)), _isInline(false) {}
    virtual ~redis_reply()
    {
        dispose();
    }

    inline bool is_valid() const
    {
        return _reply != NULL;
    }
    void dispose()
    {
        if(_reply.ref_count() < 1 && is_valid() && !_isInline)
        {
            freeReplyObject(_reply);
            _reply = NULL;
            return;
        }
        _reply.reset() = NULL;
    }

    inline int get_type() const
    {
        return _reply.obj()->type;
    }
    bool isOK() const
    {
        return get_type() == REDIS_REPLY_STATUS && 
            StringConvert::Compare(_reply.obj()->str, "OK", true);
    }
    inline long long getInt() const
    {
        return _reply.obj()->integer;
    }
    inline const char* get_string() const
    {
        return _reply.obj()->str;
    }
    inline size_t get_array(std::vector<redis_reply>& arr)
    {
        for(size_t i = 0; i < _reply.obj()->elements; ++i)
        {
            arr.push_back(redis_reply(_reply.obj()->element[i]));
            arr.back()._isInline = true;
        }
        return _reply.obj()->elements;
    }
};
/**
 * @brief redis操作上下文的封装
 * @date 2018-07-16 10:40
 */
class redis_context
{
protected:
    shared_obj<redisContext*> _ctx;
public:
    redis_context() : _ctx(NULL) {}
    virtual ~redis_context()
    {
        dispose();
    }

    bool connect(const char* ip, int port = 6379, ulong timeoutMs = 0)
    {
        dispose();
        if(timeoutMs > 0)
        {
            timeval t;
            t.tv_sec = timeoutMs / 1000;
            t.tv_usec = timeoutMs % 1000;
            _ctx = redisConnectWithTimeout(ip, port, t);
        }
        else
        {
            _ctx = redisConnect(ip, port);
        }
        return is_valid() && error() == 0;
    }
    redis_reply command(const char* format, ...)
    {
        va_list args;
        va_start(args, format);
        void* p = redisCommand(_ctx, format, args);
        va_end(args);

        return redis_reply(p);
    }

    inline int error() const
    {
        return _ctx.obj()->err;
    }
    inline const char* error_msg() const
    {
        return _ctx.obj()->errstr;
    }

    bool is_valid() const
    {
        return _ctx != NULL;
    }
    void disconnect()
    {
        redisFree(_ctx);
        _ctx = NULL;
    }
    void dispose()
    {
        if(_ctx.ref_count() < 2 && is_valid())
        {
            disconnect();
            return;
        }
        _ctx.reset() = NULL;
    }
};
//--------------------------------------------------------- 
} // namespace redis_extractor 
} // namespace wrapper
} // namespace zhou_yb 
//--------------------------------------------------------- 
#endif // _LIBZHOUYB_REDIS_EXTRACTOR_H_
//========================================================= 